#include <stdio.h>
#include <dirent.h>
#include <unistd.h>
#include <time.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <ctype.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include "rfs-defs.h"
int sdmax;

int create_socket(long port, int protocol);
int close_socket(int sd);
int read_conf();
int rls(PACKAGE*, struct sockaddr_in *);
int rcd(PACKAGE*, struct sockaddr_in *);
long get(PACKAGE*, struct sockaddr_in *);
long put(PACKAGE*, struct sockaddr_in *);
int exit_client(PACKAGE* package);
int waiting_for_connections(int sd);

int main(int argc, char *argv[]) {
    // conjuntos maestro y temporal de sockets para el select
    fd_set sockets_set, temp_s_set;
    int sd, udp_sd;

    timeout = malloc(sizeof (struct timeval));
    timeout->tv_sec = 0;
    timeout->tv_usec = TIMEOUT * 1000;

    // leo el archivo de configuración
    if (read_conf() == ERROR) {
        perror("No se ha podido leer el archivo de configuración del Servidor. Se aborta la operación.\n");
        exit(ERROR);
    };
    // Inicializo el servidor.
    // socket para recibir peticiones sobre TCP
    if ((sd = create_socket(port, SOCK_STREAM)) < 0) {
        perror("ERROR INICIALIZANDO SERVIDOR: ");
        exit(ERROR);
    }
    // socket para recibir peticiones sobre UDP
    if ((udp_sd = create_socket(port, SOCK_DGRAM)) < 0) {
        perror("ERROR INICIALIZANDO SERVIDOR: ");
        exit(ERROR);
    }

    // obtengo el máximo de los dos sockets
    sdmax = MAX(sd, udp_sd);
    printf("Servidor inicializado.\n");

    // limpio el conjunto maestro y agrego los dos sockets
    FD_ZERO(&sockets_set);
    FD_SET(sd, &sockets_set);
    FD_SET(udp_sd, &sockets_set);

    PACKAGE *package;
    package = malloc(sizeof (PACKAGE));
    int socket = 0, i;

    struct sockaddr_in from;
    int fromlen = sizeof (from);

    while (1) {
        temp_s_set = sockets_set;

        // espero petición de cliente
        if (select(sdmax + 1, &temp_s_set, NULL, NULL, NULL) == ERROR) {
            perror("ERROR en la llamada select()");
            exit(1);
        } else {
            printf("select!\n");
        }

        for (i = 0; i <= sdmax; i++) {
            if (FD_ISSET(i, &temp_s_set)) {
                if (i == sd) {
                    // atiende nuevo cliente TCP
                    if ((socket = waiting_for_connections(sd)) < 0) {
                        perror("ERROR ESPERANDO NUEVO CLIENTE: ");
                    } else {
                        FD_SET(socket, &sockets_set); // añadir al conjunto maestro
                        if (socket > sdmax) { // actualizar el máximo
                            sdmax = socket;
                        }
                    }
                } else if (i != udp_sd) {
                    // atiende socket TCP
                    read(i, package, sizeof (PACKAGE));
                    package->to = i;

                    switch (package->op) {
                        case RLS:
                            rls(package, NULL);
                            break;
                        case RCD:
                            rcd(package, NULL);
                            break;
                        case GET:
                            get(package, NULL);
                            break;
                        case PUT:
                            put(package, NULL);
                            break;
                        case QUIT:
                            printf("Cerrando cliente...\n");
                            exit_client(package);
                            FD_CLR(i, &sockets_set);
                            printf("Cliente cerrado...\n");
                            break;
                    }
                } else {
                    // atiende UDP
                    recvfrom(udp_sd, package, sizeof (PACKAGE), 0, (struct sockaddr *) & from, &fromlen);
                    package->to = udp_sd;
                    switch (package->op) {
                        case RLS:
                            rls(package, &from);
                            break;
                        case RCD:
                            rcd(package, &from);
                            break;
                        case GET:
                            get(package, &from);
                            break;
                        case PUT:
                            put(package, &from);
                            break;
                    }
                }
            }

        }
        // limpio conjunto temporal
        FD_ZERO(&temp_s_set);
    }
}